/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package mathmlcalculator;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import java.util.Stack;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import mathmlcalculator.operations.*;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;

import org.xml.sax.helpers.DefaultHandler;

enum Op {SUMA, RESTA, MULT, DIV, POT, LOG, LN, SIN, COS, TAN, ASIN, ACOS, ATAN};

/**
 *
 * @author soler4888
 */
public class SAXParserMathML extends DefaultHandler {

    private Stack<Double> operandos;
    private Stack<Op> operadores;
    private String tempVal;
    private Double xvalue;
    private String path;
    private String file;

    public SAXParserMathML(String path, String file) {
        this.path = path;
        this.file = file;
    }

    public Double run(double i) throws Exception {
        InputStream input = new FileInputStream(new File(path, file));
        operandos = new Stack<Double>();
        operadores = new Stack<Op>();
        xvalue = i;
        parseDocument(input);
        input.close();
        Double res = operandos.pop();

        if (!operadores.empty() || !operandos.empty()) {
            throw new Exception("Las pilas no están vacías");
        }

        return res;
    }

    private void parseDocument(InputStream input) {

        //get a factory
        SAXParserFactory spf = SAXParserFactory.newInstance();
        try {

            //get a new instance of parser
            SAXParser sp = spf.newSAXParser();

            //parse the file and also register this class for call backs
            sp.parse(input, this);

        } catch (SAXException se) {
            se.printStackTrace();
        } catch (ParserConfigurationException pce) {
            pce.printStackTrace();
        } catch (IOException ie) {
            ie.printStackTrace();
        }
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        if (qName.equalsIgnoreCase("plus")) {
            operadores.push(Op.SUMA);
        } else if (qName.equalsIgnoreCase("minus")) {
            operadores.push(Op.RESTA);
        } else if (qName.equalsIgnoreCase("times")) {
            operadores.push(Op.MULT);
        } else if (qName.equalsIgnoreCase("divide")) {
            operadores.push(Op.DIV);
        } else if (qName.equalsIgnoreCase("power")) {
            operadores.push(Op.POT);
        } else if (qName.equalsIgnoreCase("log")) {
            operadores.push(Op.LOG);
        } else if (qName.equalsIgnoreCase("ln")) {
            operadores.push(Op.LN);
        } else if (qName.equalsIgnoreCase("sin")) {
            operadores.push(Op.SIN);
        } else if (qName.equalsIgnoreCase("cos")) {
            operadores.push(Op.COS);
        } else if (qName.equalsIgnoreCase("tan")) {
            operadores.push(Op.TAN);
        } else if (qName.equalsIgnoreCase("arcsin")) {
            operadores.push(Op.ASIN);
        } else if (qName.equalsIgnoreCase("arccos")) {
            operadores.push(Op.ACOS);
        } else if (qName.equalsIgnoreCase("arctan")) {
            operadores.push(Op.ATAN);
        }
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        tempVal = new String(ch, start, length);
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {

        if (qName.equalsIgnoreCase("apply")) {
            Op op = operadores.pop();
            if (op.equals(Op.SUMA)) {
                MathMLSuma.Suma(operandos);
            } else if (op.equals(Op.RESTA)) {
                MathMLResta.Resta(operandos);
            } else if (op.equals(Op.MULT)) {
                MathMLMult.Mult(operandos);
            } else if (op.equals(Op.DIV)) {
                MathMLDiv.Div(operandos);
            } else if (op.equals(Op.POT)) {
                MathMLPot.Pot(operandos);
            } else if (op.equals(Op.LOG)) {
                MathMLLog.Log(operandos);
            } else if (op.equals(Op.LN)) {
                MathMLLn.Ln(operandos);
            } else if (op.equals(Op.SIN)) {
                MathMLSin.Sin(operandos);
            } else if (op.equals(Op.COS)) {
                MathMLCos.Cos(operandos);
            } else if (op.equals(Op.TAN)) {
                MathMLTan.Tan(operandos);
            } else if (op.equals(Op.ASIN)) {
                MathMLAsin.Asin(operandos);
            } else if (op.equals(Op.ACOS)) {
                MathMLAcos.Acos(operandos);
            } else if (op.equals(Op.ATAN)) {
                MathMLAtan.Atan(operandos);
            }
        } else if (qName.equalsIgnoreCase("cn")) {
            operandos.push(Double.parseDouble(tempVal));
        } else if (qName.equalsIgnoreCase("ci")) {
            if (tempVal.equalsIgnoreCase("pi")) {
                operandos.push(Math.PI);
            } else if (tempVal.equalsIgnoreCase("e")) {
                operandos.push(Math.E);
            } else if (tempVal.equalsIgnoreCase("x")) {
                operandos.push(xvalue);
            } else {
                throw new SAXException("Solo se permite una variable por ecuacion");
            }
        }
    }
}
